#include "mapcalibrator.h"
#include "configuration.h"

#include "controls/ftpushbutton.h"
#include "controls/ftpushbuttonicon.h"
#include "controls/ftlabel.h"
#include "controls/ftlatlonedit.h"
#include "controls/ftmessagebox.h"
#include "controls/ftvboxlayout.h"
#include "controls/fthboxlayout.h"

MapCalibrator::MapCalibrator(QWidget*) :
    RotatingWidget()
{
    pagesWidget = new QStackedWidget();
    map = NULL;

    buildMainPage();
    buildPointPage();

    center_x = center_y = 0;
    scale = 1;

    currentPage = main_page;
    currentState = rest;

    pagesWidget->setCurrentWidget( mainPage );

}

void MapCalibrator::buildMainPage()
{
    mainPage = new QWidget();
    pagesWidget->addWidget( mainPage );

    cancelButton = new FTPushButton( tr("Cancel") );
    connect( cancelButton, SIGNAL(clicked()),
            this, SIGNAL(abortCalibration()) );

    saveButton = new FTPushButton( tr("Save") );
    connect( saveButton, SIGNAL(clicked()),
            this, SLOT(saveCalibration()) );

    zoomInButton = new FTPushButtonIcon( QIcon(":/table/zoom_in.png"));
    connect( zoomInButton, SIGNAL(clicked()),
            this, SLOT(zoomIn()) );

    zoomOutButton = new FTPushButtonIcon( QIcon(":/table/zoom_out.png"));
    connect( zoomOutButton, SIGNAL(clicked()),
            this, SLOT(zoomOut()) );

    addPointButton = new FTPushButtonIcon( QIcon(":/calibrator/add_point.png"));
    connect( addPointButton, SIGNAL(clicked()),
            this, SLOT(addPoint()) );

    center_cross = QPixmap(":/table/cross.png");
}

void MapCalibrator::buildPointPage()
{
    pointPage = new QWidget();
    pagesWidget->addWidget( pointPage );

    pointTitle = new FTLabel();
    xLabel = new FTLabel( tr("Point X (pixel):") );
    yLabel = new FTLabel( tr("Point Y (pixel):") );
    latLonLabel = new FTLabel( tr("Point latitude/longitude:") );
    latLonEdit = new FTLatLonEdit();
    xEdit = new QLineEdit();
    yEdit = new QLineEdit();
    savePointButton = new FTPushButton( tr("Save point") );
    deletePointButton = new FTPushButtonIcon( QIcon(":/misc/delete.png"), FTControls::IconNormal );
    useGpsButton = new FTPushButton( tr("Use Tracking Table position") );

    connect( savePointButton, SIGNAL(clicked()),
            this, SLOT(savePoint()) );

    connect( deletePointButton, SIGNAL(clicked()),
            this, SLOT(deletePoint()));

    connect( useGpsButton, SIGNAL(clicked()),
            this, SLOT(fromGps()));
}

QLayout* MapCalibrator::createVertical()
{
    FTVBoxLayout* grid;

    if ( mainPage->layout() != NULL )
        delete mainPage->layout();
    grid = new FTVBoxLayout();
    FTHBoxLayout* hgrid = new FTHBoxLayout();
    hgrid->addStretch();
    hgrid->addWidget( zoomInButton );
    hgrid->addStretch();
    hgrid->addWidget( addPointButton );
    hgrid->addStretch();
    hgrid->addWidget( zoomOutButton );
    hgrid->addStretch();
    grid->addLayout( hgrid );
    grid->addStretch();
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( saveButton );
    hgrid->addWidget( cancelButton );
    grid->addLayout( hgrid );
    mainPage->setLayout( grid );

    if ( pointPage->layout() != NULL )
        delete pointPage->layout();
    grid = new FTVBoxLayout();
    grid->addWidget( pointTitle );
    grid->addStretch();
    grid->addWidget( xLabel );
    grid->addWidget( xEdit );
    grid->addWidget( yLabel );
    grid->addWidget( yEdit );
    grid->addWidget( latLonLabel );
    grid->addWidget( latLonEdit );
    grid->addWidget( useGpsButton );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( deletePointButton );
    hgrid->addWidget( savePointButton );
    grid->addLayout( hgrid );
    grid->addStretch();
    pointPage->setLayout( grid );

    grid = new FTVBoxLayout();
    grid->addWidget( pagesWidget );
    return grid;
}

QLayout* MapCalibrator::createHorizontal()
{
    FTVBoxLayout* grid;
    FTHBoxLayout* hgrid;

    if ( mainPage->layout() != NULL )
        delete mainPage->layout();
    grid = new FTVBoxLayout();
    hgrid = new FTHBoxLayout();
    hgrid->addStretch();
    hgrid->addWidget( zoomInButton );
    hgrid->addStretch();
    hgrid->addWidget( addPointButton );
    hgrid->addStretch();
    hgrid->addWidget( zoomOutButton );
    hgrid->addStretch();
    grid->addLayout( hgrid );
    grid->addStretch();
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( saveButton );
    hgrid->addWidget( cancelButton );
    grid->addLayout( hgrid );
    mainPage->setLayout( grid );

    if ( pointPage->layout() != NULL )
        delete pointPage->layout();
    grid = new FTVBoxLayout();
    grid->addWidget( pointTitle );
    grid->addStretch();
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( xLabel );
    hgrid->addWidget( xEdit );
    grid->addLayout( hgrid );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( yLabel );
    hgrid->addWidget( yEdit );
    grid->addLayout( hgrid );
    grid->addWidget( latLonLabel );
    grid->addWidget( latLonEdit );
    grid->addWidget( useGpsButton );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( deletePointButton );
    hgrid->addWidget( savePointButton );
    grid->addLayout( hgrid );
    pointPage->setLayout( grid );

    grid = new FTVBoxLayout();
    grid->addWidget( pagesWidget );
    return grid;
}

void MapCalibrator::setupCalibrator(const Map * m, const QList<map_cal_point>& pts )
{
    map = m;
    points = pts;
    selected_point = -1;
    center_x = map->pixelSize().width() / 2;
    center_y = map->pixelSize().height() / 2;
    scale = 1;
    setCurrentPage( MapCalibrator::main_page );
    currentState = rest;
}

void MapCalibrator::saveCalibration()
{
    emit mapCalibrated( points );
}

void MapCalibrator::zoomIn()
{
    if ( scale > 1 )
        scale /= 2;
    update();
}

void MapCalibrator::zoomOut()
{
    if ( scale < map->maxZoom() )
        scale *= 2;
    update();
}

void MapCalibrator::addPoint()
{
    map_cal_point new_point;
    new_point.x = center_x;
    new_point.y = center_y;
    new_point.lat = new_point.lon = 0.0;
    selected_point = points.count();
    points.append( new_point );
    openEditPoint();
    update();
}

void MapCalibrator::savePoint()
{
    if ( (selected_point < 0) || (selected_point >= points.count() ) )
        return;

    map_cal_point &pt = points[ selected_point ];

    pt.x = xEdit->text().toInt();
    pt.y = yEdit->text().toInt();
    pt.lat = latLonEdit->latitude();
    pt.lon = latLonEdit->longitude();
    setCurrentPage( MapCalibrator::main_page );
    update();
}

void MapCalibrator::deletePoint()
{
    if ( FTMessageBox::Yesno( tr("Delete point?"), tr("Confirm deletion?") ) )
    {
        points.takeAt( selected_point );
        selected_point = -1;
        setCurrentPage( MapCalibrator::main_page );
        update();
    }
}

void MapCalibrator::fromGps()
{
    double lat, lon;
    int alt;
    configuration->initialPosition( lat, lon, alt );
    latLonEdit->setLatLon( lat, lon );
}

void MapCalibrator::openEditPoint()
{
    if ( (selected_point < 0) || (selected_point >= points.count() ) )
        return;

    map_cal_point pt = points.at( selected_point );

    xEdit->setText( QString("%1").arg(pt.x) );
    yEdit->setText( QString("%1").arg(pt.y) );
    latLonEdit->setLatLon( pt.lat, pt.lon );

    setCurrentPage( MapCalibrator::point_page );
}

void MapCalibrator::setCurrentPage( MapCalibrator::pages p )
{
    currentPage = p;
    pagesWidget->setCurrentWidget( p == main_page ?  mainPage : pointPage );
}

void MapCalibrator::paintEvent(QPaintEvent *)
{
    if ( (currentPage != MapCalibrator::main_page) || (map == NULL) )
        return;

    static const QPoint marker[3] = {
        QPoint(0, 0),
        QPoint(0, -10),
        QPoint(10, -10)
    };

    QPainter painter(this);

    map->paint( painter,QRect(center_x - (width() / 2)*scale,
                              center_y - (height() / 2)*scale,
                              width() * scale,
                              height() * scale),
                         rect() );

    for (int i = 0; i < points.count(); i++ )
    {
        const map_cal_point& cp = points.at(i);
        painter.save();
        painter.translate( (cp.x - center_x)/scale + (width() / 2),
                           (cp.y - center_y)/scale + (height() / 2) );

        QRect point_rect = QRect( 0,-40,30,30 );
        painter.setBrush( QBrush( QColor("red") ) );
        painter.setPen( QColor("red") );
        painter.drawConvexPolygon( marker, 3);
        painter.drawRect( point_rect.adjusted(-1, -1, 1, 1) );
        painter.setPen( QColor("white") );
        painter.fillRect( point_rect, QBrush( QColor("red") ) );
        painter.drawText( point_rect, Qt::AlignCenter, QString("%1").arg(i+1) );
        painter.setBrush( Qt::NoBrush );
        if ( selected_point == i )
            painter.drawRect( -10, -50, 60, 60 );
        painter.restore();
    }
    QRect r = rect();
    r.setTop( r.top() + 100 );
    painter.drawText( r, Qt::AlignHCenter | Qt::AlignVCenter, QString("x:%1,y:%2").arg( center_x ).arg( center_y )) ;

    painter.drawPixmap( width()/2-center_cross.width()/2, height()/2-center_cross.height()/2, center_cross );
}

void MapCalibrator::mousePressEvent(QMouseEvent * me)
{
    if ( currentPage != MapCalibrator::main_page )
        return;

    currentState = MapCalibrator::rest;

    // Store starting position...
    mouse_start = me->pos();
    mouseMoving = true;

    selected_point = -1;
    // Lets try to locate if we are on top of a point...
    for (int i = 0; i < points.count(); i++ )
    {
        const map_cal_point& cp = points.at(i);
        QPoint pt_to_screen = QPoint( cp.x - center_x, cp.y - center_y ) / scale;
        QPoint distance_vector = pt_to_screen + QPoint( width() / 2 + 15, height() / 2 - 20) - mouse_start;
        if ( distance_vector.manhattanLength() < FTControls::shakyThreeshold()*2 )
        {
            selected_point = i;
            break;
        }
    }
    update();
}

void MapCalibrator::mouseReleaseEvent(QMouseEvent *)
{
    if ( currentPage != MapCalibrator::main_page )
        return;

    mouseMoving = false;

    // Only if we have a valid selected point...
    if ( (selected_point > -1) &&
         (selected_point < points.count() ) )
    {
        // When we "drag" a point,
        if ( currentState == MapCalibrator::drag_point )
        {
            // nothing
        }
        else if ( currentState == MapCalibrator::rest )
        {
            openEditPoint();
        }
    }
    currentState = MapCalibrator::rest;
}

void MapCalibrator::mouseMoveEvent(QMouseEvent * me)
{
    if ( currentPage != MapCalibrator::main_page )
        return;

    QPoint map_delta = (me->pos() - mouse_start) * scale;

    if ( currentState == MapCalibrator::rest )
    {
        // Check if we must go from "rest" to another state...
        if ( map_delta.manhattanLength() > FTControls::shakyThreeshold()*scale )
        {
            if ( selected_point > -1 )
                currentState = MapCalibrator::drag_point;
            else
                currentState = MapCalibrator::move_screen;
        }
    }

    if ( currentState == MapCalibrator::move_screen )
    {
        center_x -= map_delta.x();
        center_y -= map_delta.y();
        update();
    }
    else if ( currentState == MapCalibrator::drag_point )
    {
        map_cal_point& pt = points[ selected_point ];
        pt.x += map_delta.x();
        pt.y += map_delta.y();
        update();
    }
    mouse_start = me->pos();
}
